package com.xxxtea.util;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.xxxtea.config.converter.ListConverter;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.springframework.http.HttpHeaders;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Excel工具类
 * 
 * 基于EasyExcel框架提供的Excel导入导出功能，包括：
 * - Excel文件导入：支持将Excel文件数据转换为Java对象列表
 * - Excel文件导出：支持将Java对象列表导出为Excel文件
 * - Excel模板创建：支持创建带有表头的Excel模板文件
 * - 自定义样式：统一的Excel样式配置
 * 
 * 主要特性：
 * - 支持Swagger注解自动生成表头
 * - 统一的字体和样式配置
 * - 自动列宽调整
 * - 支持中文文件名编码
 * 
 * @author badman
 * @since 1.0.0
 */
@Slf4j
@UtilityClass
public class ExcelUtils {
	
	/**
	 * 默认的Excel样式策略
	 * 包含表头样式和内容样式的统一配置
	 */
	private static final HorizontalCellStyleStrategy DEFAULT_STRATEGY;

	/**
	 * 静态初始化块：配置默认的Excel样式
	 * 包括字体、表头样式、内容样式等
	 */
	static {
		// 设置字体：使用阿里巴巴普惠体，10号字体，黑色
		WriteFont font = new WriteFont();
		font.setFontName("阿里巴巴普惠体");
		font.setFontHeightInPoints((short) 10);
		font.setColor(IndexedColors.BLACK.index);

		// 设置头部样式：标题居中对齐
		WriteCellStyle headStyle = new WriteCellStyle();
		headStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
		headStyle.setWriteFont(font);

		// 设置内容样式：内容居中对齐，支持自动换行
		WriteCellStyle contentStyle = new WriteCellStyle();
		contentStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
		contentStyle.setWrapped(true);
		
		// 创建样式策略
		DEFAULT_STRATEGY = new HorizontalCellStyleStrategy(headStyle, contentStyle);
	}

	/**
	 * 导入Excel表格
	 * 
	 * 将上传的Excel文件解析为指定类型的Java对象列表
	 * 
	 * @param file 上传的Excel文件
	 * @param clazz 目标Java类型
	 * @param <T> 泛型类型
	 * @return 解析后的Java对象列表
	 * @throws IOException 文件读取异常
	 */
	public <T> List<T> importExcel(MultipartFile file, Class<T> clazz) throws IOException {
		// 使用EasyExcel读取文件，自动映射到指定类型
		List<T> dataList = EasyExcel.read(file.getInputStream())
				.head(clazz)
				.sheet()
				.doReadSync();
		log.info("成功导入[{}]条数据", dataList.size());
		return dataList;
	}

	/**
	 * 导出Excel表格到HTTP响应
	 * 
	 * 将Java对象列表导出为Excel文件并通过HTTP响应下载
	 * 
	 * @param response HTTP响应对象
	 * @param dataList 要导出的数据列表
	 * @param clazz 数据类型
	 * @param title 文件名标题
	 * @param <T> 泛型类型
	 * @throws IOException 文件写入异常
	 */
	public <T> void exportExcel(HttpServletResponse response, List<T> dataList, Class<T> clazz, String title) throws IOException {
		// 设置下载文件名：标题_时间戳.xls
		String timestamp = DateUtil.format(new Date(), DatePattern.CHINESE_DATE_TIME_PATTERN);
		String filename = URLUtil.encodeAll(title + "_" + timestamp + ExcelTypeEnum.XLS.getValue(), StandardCharsets.UTF_8);
		response.setHeader(HttpHeaders.CONTENT_DISPOSITION, StrUtil.format("attachment;filename=\"{}\";", filename));

		// 开始导出Excel：使用默认样式策略
		EasyExcel.write(response.getOutputStream(), clazz)
				.excelType(ExcelTypeEnum.XLS)
				.registerWriteHandler(DEFAULT_STRATEGY)
				.sheet(title)
				.doWrite(dataList);

		log.info("成功导出[{}]条数据", dataList.size());
	}

	/**
	 * 创建Excel模板文件
	 * 
	 * 创建一个只包含表头的Excel模板文件，用于数据导入
	 * 
	 * @param response HTTP响应对象
	 * @param clazz 数据类型
	 * @param title 文件名标题
	 * @param excludeColumnFieldNames 要排除的字段名数组
	 * @param <T> 泛型类型
	 * @throws IOException 文件写入异常
	 */
	public <T> void createTemplate(HttpServletResponse response, Class<T> clazz, String title, String... excludeColumnFieldNames) throws IOException {
		// 构建排除字段集合：包含传入的字段和默认排除的时间字段
		Set<String> excludeColumnSet = Arrays.stream(excludeColumnFieldNames).collect(Collectors.toSet());
		excludeColumnSet.addAll(Arrays.asList("createTime", "updateTime"));

		// 设置下载文件名
		String filename = URLUtil.encodeAll(title + ExcelTypeEnum.XLS.getValue(), StandardCharsets.UTF_8);
		response.setHeader(HttpHeaders.CONTENT_DISPOSITION, StrUtil.format("attachment;filename=\"{}\";", filename));
		
		// 创建模板：使用空数据列表，只生成表头
		EasyExcel.write(response.getOutputStream(), clazz)
				.excelType(ExcelTypeEnum.XLS)
				.registerWriteHandler(DEFAULT_STRATEGY)
				.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) // 自动调整列宽
				.excludeColumnFieldNames(excludeColumnSet)
				.sheet(title)
				.doWrite(Collections.emptyList());
		log.info("创建excel模版 [{}], 忽略字段: {}", title, excludeColumnSet);
	}

	/**
	 * 根据Swagger注解导出Excel到输出流
	 * 
	 * 通过反射获取类上的@ApiModel和字段上的@ApiModelProperty注解
	 * 自动生成表头和数据内容
	 * 
	 * @param outputStream 输出流
	 * @param dataList 要导出的数据列表
	 * @param clazz 数据类型
	 * @param <T> 泛型类型
	 * @throws Exception 反射或文件写入异常
	 */
	public <T> void exportExcel(OutputStream outputStream, List<T> dataList, Class<T> clazz) throws Exception {
		List<List<String>> head = new ArrayList<>();
		List<List<Object>> rows = new ArrayList<>();

		// 获取类上的Swagger注解，用于设置工作表名称
		ApiModel apiModel = clazz.getAnnotation(ApiModel.class);
		String sheetName = apiModel != null ? apiModel.value() : clazz.getSimpleName();

		// 通过反射获取字段上的Swagger注解，生成表头
		Field[] fields = clazz.getDeclaredFields();
		for (Field field : fields) {
			ApiModelProperty apiModelProperty = field.getAnnotation(ApiModelProperty.class);
			if (apiModelProperty != null) {
				List<String> headColumn = new ArrayList<>();
				headColumn.add(apiModelProperty.value());
				head.add(headColumn);
			}
		}

		// 填充数据行：通过反射获取对象字段值
		for (Object obj : dataList) {
			List<Object> row = new ArrayList<>();
			for (Field field : fields) {
				field.setAccessible(true); // 设置字段可访问
				row.add(field.get(obj));
			}
			rows.add(row);
		}

		// 导出Excel：使用自定义表头和数据
		EasyExcel.write(outputStream)
				.registerConverter(new ListConverter()) // 注册列表转换器
				.excelType(ExcelTypeEnum.XLS)
				.registerWriteHandler(DEFAULT_STRATEGY)
				.head(head)
				.sheet(sheetName)
				.doWrite(rows);
		log.info("根据ApiModelProperty, 成功导出[{}]条数据", dataList.size());
	}
}
